// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/libplatform/default-foreground-task-runner.h"

#include "src/base/platform/mutex.h"
#include "src/libplatform/default-platform.h"

namespace v8 {
namespace platform {

    DefaultForegroundTaskRunner::DefaultForegroundTaskRunner(
        IdleTaskSupport idle_task_support, TimeFunction time_function)
        : idle_task_support_(idle_task_support)
        , time_function_(time_function)
    {
    }

    void DefaultForegroundTaskRunner::Terminate()
    {
        base::MutexGuard guard(&lock_);
        terminated_ = true;

        // Drain the task queues.
        while (!task_queue_.empty())
            task_queue_.pop();
        while (!delayed_task_queue_.empty())
            delayed_task_queue_.pop();
        while (!idle_task_queue_.empty())
            idle_task_queue_.pop();
    }

    void DefaultForegroundTaskRunner::PostTaskLocked(std::unique_ptr<Task> task,
        const base::MutexGuard&)
    {
        if (terminated_)
            return;
        task_queue_.push(std::move(task));
        event_loop_control_.NotifyOne();
    }

    void DefaultForegroundTaskRunner::PostTask(std::unique_ptr<Task> task)
    {
        base::MutexGuard guard(&lock_);
        PostTaskLocked(std::move(task), guard);
    }

    double DefaultForegroundTaskRunner::MonotonicallyIncreasingTime()
    {
        return time_function_();
    }

    void DefaultForegroundTaskRunner::PostDelayedTask(std::unique_ptr<Task> task,
        double delay_in_seconds)
    {
        DCHECK_GE(delay_in_seconds, 0.0);
        base::MutexGuard guard(&lock_);
        if (terminated_)
            return;
        double deadline = MonotonicallyIncreasingTime() + delay_in_seconds;
        delayed_task_queue_.push(std::make_pair(deadline, std::move(task)));
    }

    void DefaultForegroundTaskRunner::PostIdleTask(std::unique_ptr<IdleTask> task)
    {
        CHECK_EQ(IdleTaskSupport::kEnabled, idle_task_support_);
        base::MutexGuard guard(&lock_);
        if (terminated_)
            return;
        idle_task_queue_.push(std::move(task));
    }

    bool DefaultForegroundTaskRunner::IdleTasksEnabled()
    {
        return idle_task_support_ == IdleTaskSupport::kEnabled;
    }

    std::unique_ptr<Task> DefaultForegroundTaskRunner::PopTaskFromQueue(
        MessageLoopBehavior wait_for_work)
    {
        base::MutexGuard guard(&lock_);
        // Move delayed tasks that hit their deadline to the main queue.
        std::unique_ptr<Task> task = PopTaskFromDelayedQueueLocked(guard);
        while (task) {
            PostTaskLocked(std::move(task), guard);
            task = PopTaskFromDelayedQueueLocked(guard);
        }

        while (task_queue_.empty()) {
            if (wait_for_work == MessageLoopBehavior::kDoNotWait)
                return {};
            WaitForTaskLocked(guard);
        }

        task = std::move(task_queue_.front());
        task_queue_.pop();

        return task;
    }

    std::unique_ptr<Task>
    DefaultForegroundTaskRunner::PopTaskFromDelayedQueueLocked(
        const base::MutexGuard&)
    {
        if (delayed_task_queue_.empty())
            return {};

        double now = MonotonicallyIncreasingTime();
        const DelayedEntry& deadline_and_task = delayed_task_queue_.top();
        if (deadline_and_task.first > now)
            return {};
        // The const_cast here is necessary because there does not exist a clean way
        // to get a unique_ptr out of the priority queue. We provide the priority
        // queue with a custom comparison operator to make sure that the priority
        // queue does not access the unique_ptr. Therefore it should be safe to reset
        // the unique_ptr in the priority queue here. Note that the DelayedEntry is
        // removed from the priority_queue immediately afterwards.
        std::unique_ptr<Task> result = std::move(const_cast<DelayedEntry&>(deadline_and_task).second);
        delayed_task_queue_.pop();
        return result;
    }

    std::unique_ptr<IdleTask> DefaultForegroundTaskRunner::PopTaskFromIdleQueue()
    {
        base::MutexGuard guard(&lock_);
        if (idle_task_queue_.empty())
            return {};

        std::unique_ptr<IdleTask> task = std::move(idle_task_queue_.front());
        idle_task_queue_.pop();

        return task;
    }

    void DefaultForegroundTaskRunner::WaitForTaskLocked(const base::MutexGuard&)
    {
        event_loop_control_.Wait(&lock_);
    }

} // namespace platform
} // namespace v8
